package com.xxxx.spring.reflect;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ComponentScan(basePackages = "com.xxxx.spring") // 包括子包下的类
public class Container {

    private Map<Class<?>, Method> methods;

    private Object config;

    // 初始化时创建这个对象
    private Map<Class<?>,Object> services;

    public void init() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        this.methods = new HashMap<>();
        this.services = new HashMap<>();
        Class<?> clazz = Class.forName("com.xxxx.demo.domain.Config");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if(method.getDeclaredAnnotation(Bean.class) != null){
                this.methods.put(method.getReturnType(), method);
            }
        }
        this.config = clazz.getConstructor().newInstance();
    }

    public Object getServiceInstanceByClass(Class<?> clazz) throws InvocationTargetException, IllegalAccessException {
        if(this.services.containsKey(clazz)){
            return this.services.get(clazz);
        }else {
            if(this.methods.containsKey(clazz)){
                Method method = this.methods.get(clazz);
                // 在这个对象(this.config)上调用 method 代表的方法 返回的值赋给 Object
                Object obj = method.invoke(this.config);
                this.services.put(clazz,obj);
                return obj;
            }
        }
        return null;
    }

    public Object createInstance(Class<?> clazz) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for(Constructor<?> constructor : constructors){
            if(constructor.getDeclaredAnnotation(Autowired.class) != null){
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                Object[] arguments = new Object[parameterTypes.length];
                for(int i = 0; i < parameterTypes.length; i++){
                    arguments[i] = getServiceInstanceByClass(parameterTypes[i]);
                }
                return constructor.newInstance(arguments);
            }
        }
        return clazz.getConstructor().newInstance();
    }
}
